package gui.teamEditor;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.text.NumberFormat;
import java.text.ParseException;

import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;

import data.Player;
import data.Team;
import data.Data.Skill;

/**
 * A single player's GUI representation. Imports data from a Player object,
 * and writes it back to the Player object
 * 
 * @author Julien
 *
 */
public class PlayerPanel extends JPanel {
	private static final long serialVersionUID = -9020223573432746435L;
	
	// Actual data we'll be working on
	private Player m_player;
	private Team m_team; // Needed for some validation (player number)
	
	// GUI elements
	JLabel m_nameLabel = new JLabel("Name");
	JTextField m_name = new JTextField();
	
	JLabel m_typeLabel = new JLabel("Role");
	JLabel m_type = new JLabel("");
	
	JLabel m_numberLabel = new JLabel("Number");
	JFormattedTextField m_number = new JFormattedTextField(NumberFormat.getInstance());
	PlayerNumberVerifier m_numberVerifier = null;
	
	JLabel m_levelLabel = new JLabel("Level");
	JFormattedTextField m_level = new JFormattedTextField(NumberFormat.getInstance());
	
	JLabel m_experienceLabel = new JLabel("Experience");
	JFormattedTextField m_experience = new JFormattedTextField(NumberFormat.getInstance());
	
	JLabel m_emptyLabel = new JLabel("");
	
	JLabel m_experienceToGoLabel = new JLabel("");
	
	JLabel m_MALabel = new JLabel("MA");
	JFormattedTextField m_MA = new JFormattedTextField(NumberFormat.getInstance());

	JLabel m_STLabel = new JLabel("ST");
	JFormattedTextField m_ST = new JFormattedTextField(NumberFormat.getInstance());
	
	JLabel m_AGLabel = new JLabel("AG");
	JFormattedTextField m_AG = new JFormattedTextField(NumberFormat.getInstance());
	
	JLabel m_AVLabel = new JLabel("AV");
	JFormattedTextField m_AV = new JFormattedTextField(NumberFormat.getInstance());
	
	JLabel m_skillsLabel = new JLabel("Skills");
	JList m_skills = new JList(Skill.values());
	
	/**
	 * Creates a player panel containing the data from the player
	 * @param player The player that we will use to load/save data to.
	 * @param team The team in which the player is (needed to check the player number)
	 */
	public PlayerPanel(Player player, Team team)
	{
		m_player = player;
		m_team   = team;
		
		// First, setup the data
		m_name.setText(m_player.name());
		m_type.setText(m_player.race().toString() + " " + m_player.role().toString() );
		m_number.setText(Integer.toString(m_player.number()));
		m_level.setText(Integer.toString(m_player.level()));
		m_experience.setText(Integer.toString(m_player.XP()));
		m_experienceToGoLabel.setText("For next level: " + (player.level() >= 7 ? "infinite" : player.nextLevelXP()) );
		m_MA.setText(Integer.toString(m_player.MA()));
		m_ST.setText(Integer.toString(m_player.ST()));
		m_AG.setText(Integer.toString(m_player.AG()));
		m_AV.setText(Integer.toString(m_player.AV()));
		
		m_numberVerifier = new PlayerNumberVerifier(m_player, m_team);
		m_number.setInputVerifier(m_numberVerifier);
		
		// Set skills
		m_skills.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		m_skills.setLayoutOrientation(JList.VERTICAL);
		m_skills.setVisibleRowCount(Skill.values().length);
		
		// Since JList doesn't have a "setSelectedValues" method, we need to pass an array of ints instead.
		// Fortunately, enums have an ordinal value that makes it easier to do.
		int skillIndex = 0;
		int skillsSelected[] = new int[m_player.skills().size()];
		for( Skill skill : m_player.skills() )
			skillsSelected[skillIndex++] = skill.ordinal();
		
		m_skills.setSelectedIndices(skillsSelected);
				
		// Prepare panel layout
		setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridheight = 1;
		c.gridwidth = 1;
		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 1;
		c.weighty = 1;
		
		// Add left hand first (labels)
		add(m_nameLabel,c);		
		c.gridy = GridBagConstraints.RELATIVE;
		add(m_typeLabel,c);
		add(m_numberLabel,c);
		add(m_levelLabel,c);
		add(m_experienceLabel,c);
		add(m_emptyLabel,c);
		add(m_MALabel,c);
		add(m_STLabel,c);
		add(m_AGLabel,c);
		add(m_AVLabel,c);
		add(m_skillsLabel,c);
		
		// Then add everything to the panel (this)		
		c.gridx = 1;
		c.gridy = 0;
		add(m_name,c);
		c.gridy = GridBagConstraints.RELATIVE;
		add(m_type,c);		
		add(m_number,c);		
		add(m_level,c);		
		add(m_experience,c);
		add(m_experienceToGoLabel,c);		
		add(m_MA,c);		
		add(m_ST,c);		
		add(m_AG,c);		
		add(m_AV,c);		
		
		c.gridheight = Skill.values().length;
		add(m_skills,c);
	}
	
	/**
	 * Returns the player that this panel refers to.
	 * @return Reference to the player referenced in the panel.
	 */
	public Player getPlayer()
	{
		return m_player;
	}
	
	/**
	 * This method reads the data from the text fields and saves it in the Player object,
	 * prior to the team serialization.
	 */
	public void commit()
	{
		m_player.setName(m_name.getText());
		
		try
		{
			m_number.commitEdit();
			m_player.setNumber(Integer.parseInt(m_number.getText()));
		} catch( ParseException e )
		{
			// ...
		}
		
		try
		{
			m_experience.commitEdit();
			m_player.setXP(Integer.parseInt(m_experience.getValue().toString()));
		} catch( ParseException e )
		{
			// ...
		}
		
		try
		{
			m_level.commitEdit();
			m_player.setLevel(Integer.parseInt(m_level.getValue().toString()));
		} catch( ParseException e )
		{
			// ...
		}
		
		try
		{
			m_MA.commitEdit();
			m_player.setMA(Integer.parseInt(m_MA.getValue().toString()));
		} catch( ParseException e )
		{
			// ...
		}
		
		try
		{
			m_ST.commitEdit();
			m_player.setST(Integer.parseInt(m_ST.getValue().toString()));
		} catch( ParseException e )
		{
			// ...
		}
		
		try
		{
			m_AG.commitEdit();
			m_player.setAG(Integer.parseInt(m_AG.getValue().toString()));
		} catch( ParseException e )
		{
			// ...
		}
		
		try
		{
			m_AV.commitEdit();
			m_player.setAV(Integer.parseInt(m_AV.getValue().toString()));
		} catch( ParseException e )
		{
			// ...
		}
		
		m_player.removeAllSkills();
		Object[] selectedSkills = m_skills.getSelectedValues();
		for( Object skill : selectedSkills )
			m_player.addSkill( (Skill)skill);
	}

	/**
	 * Internal class to verify that the "new" player number we assign is valid (i.e. no duplicates) 
	 * @author Julien
	 *
	 */
	class PlayerNumberVerifier extends InputVerifier
	{
		protected Player m_player = null;
		protected Team   m_team   = null;		
		
		PlayerNumberVerifier( Player player, Team team )
		{
			m_player = player;
			m_team   = team;
		}
		
		@Override
		public boolean shouldYieldFocus(JComponent input)
		{
			boolean inputOK = verify(input);
			
			if(!inputOK)
			{
				JOptionPane.showMessageDialog(input, "Please select a valid number that isn't already used in the team.", "Player number change error", JOptionPane.ERROR_MESSAGE);
				
				JFormattedTextField field = (JFormattedTextField)input;
				if( field.getValue() != null )
					field.setText( field.getValue().toString() );
			}
			
			return inputOK;
		}

		@Override
		public boolean verify(JComponent input) {
			JFormattedTextField field = (JFormattedTextField)input;
			try
			{
				int number = Integer.parseInt(field.getText());				
				Player playerUsingThisNumber = m_team.getPlayerByNumber(number);
				
				return (playerUsingThisNumber == null || playerUsingThisNumber == m_player);
			}
			catch( NumberFormatException e )
			{
				return false;
			}
		}		
	}
}
